-
Notifications
You must be signed in to change notification settings - Fork 299
[v1.3] userScripts / scripting API 调整,增强兼容性 ( 重做 #704 ) #925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
96352e8 to
deea0bc
Compare
4f9723b to
01bb9fa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
此 PR 重做了 #704 的修改,解决了 userScripts 和 scripting API 的兼容性问题。主要目标是适配 Firefox 和 Chrome 在脚本注入机制上的差异:Firefox 不支持动态代码注入,需要通过 URL 参数传递 MessageFlag;而 Chrome 不支持 URL 查询参数,继续使用 userScripts API 的代码注入方式。
- 引入
getUspMessageFlag()函数从错误堆栈中提取 URL 参数 - 重构脚本注册逻辑,区分 Firefox(scripting API)和 Chrome(userScripts API)两种注册方式
- 更新 ID 检查从 "scriptcat-content" 改为 "scriptcat-inject"
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 12 comments.
| File | Description |
|---|---|
src/pkg/utils/utils.ts |
新增 getUspMessageFlag() 函数用于从堆栈提取 URL 参数;更新 checkUserScriptsAvailable() 的 ID 检查 |
src/content.ts |
更新 MessageFlag 获取逻辑,支持从 arguments 或 URL 参数中获取;改进错误处理和注释说明 |
src/app/service/service_worker/runtime.ts |
重构 getContentAndInjectScript() 返回分离的 content 和 inject 脚本列表;Firefox 使用 scripting API 注册 content.js(URL参数),Chrome 使用 userScripts API(代码注入);更新注册和检查逻辑;移除 compileInjectUserScript() 方法 |
| // getContentAndInjectScript依赖loadScriptMatchInfo | ||
| // 需要等getParticularScriptList完成后再执行 | ||
| const generalScriptList = await this.getContentAndInjectScript(options); | ||
| const { inject: injectScripList, content: contentScriptList } = await this.getContentAndInjectScript(options); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
变量名拼写错误(与第920行相同):injectScripList 应该是 injectScriptList(缺少字母 't')。
| } | ||
| const retScript: chrome.userScripts.RegisteredUserScript[] = []; | ||
| const contentJs = await this.getContentJsCode(); | ||
| if (contentJs) { | ||
| const codeBody = `(function (MessageFlag) {\n${contentJs}\n})('${messageFlag}')`; | ||
| const code = `${codeBody}${sourceMapTo("scriptcat-content.js")}\n`; | ||
| retScript.push({ | ||
| id: "scriptcat-content", | ||
| js: [{ code }], | ||
| matches: ["<all_urls>"], | ||
| allFrames: true, | ||
| runAt: "document_start", | ||
| world: "USER_SCRIPT", | ||
| excludeMatches, | ||
| excludeGlobs, | ||
| }); | ||
| } | ||
|
|
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
返回类型的命名不够清晰:函数返回对象的键名 content 和 inject 没有明确表明它们分别对应不同的 API。
考虑到:
content仅在 Firefox 中使用,通过scripting.RegisteredContentScriptinject在两个浏览器中使用,通过userScripts.RegisteredUserScript
建议改进返回对象的结构或添加 JSDoc 注释,明确说明:
content: 用于 Firefox 的 scripting API 注册的脚本列表inject: 用于 userScripts API 注册的脚本列表(包括 inject.js 和 Chrome 下的 content.js)
这样可以提高代码的可读性和可维护性。
| // 判断是否已经注册过 | ||
| if (runtimeGlobal.registered) { | ||
| // 异常情况 | ||
| // 检查scriptcat-content和scriptcat-inject是否存在 |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
注释与代码不一致:注释提到"检查scriptcat-content和scriptcat-inject是否存在",但代码第895行只检查了 "scriptcat-inject"。
由于新架构中:
- Firefox: scriptcat-content 通过 scripting API 注册(不在 userScripts 中)
- Chrome: scriptcat-content 通过 userScripts API 注册(在 userScripts 中)
建议更新注释以准确反映实际检查的内容,例如:"检查scriptcat-inject是否存在"
| // 检查scriptcat-content和scriptcat-inject是否存在 | |
| // 检查scriptcat-inject是否存在 | |
| // 说明:scriptcat-content 在 Firefox 通过 scripting API 注册(不在 userScripts 中), | |
| // 在 Chrome 通过 userScripts API 注册(在 userScripts 中),此处仅检查 scriptcat-inject 是否存在 |
| export const getUspMessageFlag = () => { | ||
| const s = new Error().stack; | ||
| if (s) { | ||
| const search1 = "content.js?usp_flag="; | ||
| const len1 = search1.length; | ||
| const idx1 = s.indexOf(search1); | ||
| if (idx1 > 0) { | ||
| const search2 = "&usp_end"; | ||
| const idx2 = s.indexOf(search2, idx1 + len1); | ||
| if (idx2 > 0) { | ||
| const param = s.substring(idx1 + len1, idx2); | ||
| try { | ||
| // 使用 URLSearchParams 避免字符编码问题 | ||
| const uspString = `usp_flag=${param}`; | ||
| const usp = new URLSearchParams(uspString); | ||
| if (usp.size === 1) return usp.get("usp_flag") || null; | ||
| } catch (e) { | ||
| console.error(e); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return null; | ||
| }; |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
新增的 getUspMessageFlag() 函数缺少测试覆盖。该函数通过解析错误堆栈提取 URL 参数,属于核心功能逻辑,应该有测试确保:
- 能够正确从堆栈中提取
usp_flag参数 - 正确处理 URLSearchParams 解码
- 对于无效或缺失的参数返回
null - 正确处理错误情况
建议在 src/pkg/utils/utils.test.ts 中添加相应的测试用例。
| retContent = [ | ||
| { | ||
| id: "scriptcat-content", | ||
| js: [`/src/content.js?${new URLSearchParams({ usp_flag: messageFlag })}&usp_end`], |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
URL 构建方式不够清晰:使用字符串拼接 ?${...}&usp_end 的方式可能让人混淆 &usp_end 的意图。
虽然 &usp_end 是一个用于标记参数结束位置的标识符(用于从堆栈字符串中提取),但这种直接拼接的方式不够直观。建议:
- 添加注释说明
&usp_end的作用 - 或者使用更清晰的方式构建,例如:
js: [`/src/content.js?${new URLSearchParams({ usp_flag: messageFlag, usp_end: '' })}`]这样可以让代码意图更明确,便于后续维护。
| js: [`/src/content.js?${new URLSearchParams({ usp_flag: messageFlag })}&usp_end`], | |
| // 使用 URLSearchParams 构建 usp_flag 和 usp_end 参数,避免字符串拼接歧义 | |
| js: [`/src/content.js?${new URLSearchParams({ usp_flag: messageFlag, usp_end: "" })}`], |
| if (res.length === 1) { | ||
| return; | ||
| } | ||
| // scriptcat-content/scriptcat-inject不存在的情况 |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
注释不准确:注释提到 "scriptcat-content/scriptcat-inject不存在的情况",但代码实际上只检查了 scriptcat-inject 的存在性(第895行)。
建议更新注释为:"scriptcat-inject不存在的情况",以准确反映实际逻辑。
| // scriptcat-content/scriptcat-inject不存在的情况 | |
| // scriptcat-inject不存在的情况 |
| const search1 = "content.js?usp_flag="; | ||
| const len1 = search1.length; | ||
| const idx1 = s.indexOf(search1); | ||
| if (idx1 > 0) { |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
条件检查 idx1 > 0 应该使用 idx1 >= 0。
String.indexOf() 在找到匹配时返回索引位置(可能为0),找不到时返回-1。当搜索字符串在位置0时,当前条件会错过该匹配。虽然在实际场景中 "content.js?usp_flag=" 不太可能出现在堆栈字符串的开头(位置0),但使用 >= 0 更符合 indexOf 的标准用法,提高代码的正确性和可读性。
| if (idx1 > 0) { | |
| if (idx1 >= 0) { |
| if (idx1 > 0) { | ||
| const search2 = "&usp_end"; | ||
| const idx2 = s.indexOf(search2, idx1 + len1); | ||
| if (idx2 > 0) { |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
条件检查 idx2 > 0 应该使用 idx2 > idx1 + len1。
当前条件只检查 idx2 > 0,这意味着如果 "&usp_end" 出现在 "content.js?usp_flag=" 之前(虽然不太可能),代码仍会尝试提取参数,导致 substring(idx1 + len1, idx2) 返回空字符串或负索引。更正确的检查应该是 idx2 > idx1 + len1,确保 "&usp_end" 在 "content.js?usp_flag=" 之后。
| if (idx2 > 0) { | |
| if (idx2 > idx1 + len1) { |
|
|
||
| /* global MessageFlag */ | ||
| // @ts-ignore | ||
| const MessageFlag: string | null = (typeof arguments === "object" && arguments?.[0]) || getUspMessageFlag(); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
类型检查不够严谨:条件 typeof arguments === "object" 应该使用 typeof arguments === "object" && arguments !== null。
虽然 arguments 在实际运行中不太可能为 null,但 typeof null === "object" 在 JavaScript 中为 true,这是一个已知的语言特性。建议添加 null 检查以确保代码的健壮性和准确性。
| const MessageFlag: string | null = (typeof arguments === "object" && arguments?.[0]) || getUspMessageFlag(); | |
| const MessageFlag: string | null = (typeof arguments === "object" && arguments !== null && arguments?.[0]) || getUspMessageFlag(); |
概述 Descriptions
chrome.scripting.registerContentScripts#704之前
@early-start的代码修改,导致 #704 的修改被倒回去了现在重做 #704
历史原因,
scripting.RegisteredContentScript的设计不是用来做 自订脚本 用途,不支持动态代码。它只支持
url网址,,而不支持code代码这个PR做了在网址取 flags 的处理,因此不需要 code
但相反 chrome 不支持网址参数,因此还是用 userScripts API 做注入
已测试过能在 Firefox & Chrome 顺利执行
变更内容 Changes
截图 Screenshots